package org.dromara.system.util.kq;

import cn.hutool.core.codec.Base64Decoder;
import cn.hutool.core.lang.Console;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.crypto.asymmetric.Sign;
import cn.hutool.crypto.asymmetric.SignAlgorithm;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.json.JSONConfig;
import cn.hutool.json.JSONUtil;
import org.dromara.common.core.exception.BusinessException;
import org.dromara.system.domain.ProductLineInformation;
import org.dromara.system.domain.bo.kq.DownstreamApiBean;

import java.io.Serializable;

public class DownstreamApiSecurityUtil {

    public static <T extends Serializable> T validSignAndGetBody(
        DownstreamApiBean apiBean,
        ProductLineInformation productLineInformation,
        Class<T> type) throws Exception {

        validSign(apiBean, productLineInformation);

        return getBody(apiBean, productLineInformation, type);
    }

    public static void validSign(DownstreamApiBean apiBean, ProductLineInformation productLineInformation) throws Exception {
        if (apiBean.getHead() == null || apiBean.getSign() == null) {
            throw new Exception("报文不符合规范，请检查头与签名参数！");
        }
        String signText = toJsonStr(apiBean.getHead()) + apiBean.getBody();
        boolean isVerify = verify(
            "RSA2",
            signText,
            apiBean.getSign(),
            productLineInformation.getPlatformPubKey()
        );
        Console.log("sign verify is:{}", isVerify);
        if (!isVerify) {
            throw new Exception("验签未通过！");
        }
    }

    public static String toJsonStr(Object object) {
        return JSONUtil.toJsonStr(object, JSONConfig.create().setNatureKeyComparator());
    }

    public static <T extends Serializable> T getBody(
        DownstreamApiBean apiBean,
        ProductLineInformation productLineInformation,
        Class<T> type) {
        // 用AES密钥解密报文体
        String respAesBody = decryptSymmetryStr(
            "AES",
            decryptSecurityKey(apiBean, productLineInformation),
            decryptSecurityOffSet(apiBean, productLineInformation),
            apiBean.getBody()
        );

        System.out.println("respAesBody:" + respAesBody);
        return JSONUtil.toBean(respAesBody, type);
    }

    private static byte[] decryptSecurityKey(DownstreamApiBean apiBean, ProductLineInformation productLineInformation) {
        return decryptAsymmetric(apiBean.getHead().getSecurityKey(), productLineInformation.getAgentPriKey());
    }

    private static byte[] decryptSecurityOffSet(DownstreamApiBean apiBean, ProductLineInformation productLineInformation) {
        return decryptAsymmetric(apiBean.getHead().getSecurityOffSet(), productLineInformation.getAgentPriKey());
    }


    /**
     * 非对称
     * 解密
     *
     * @author lxp
     * @since 2023/4/23/023 17:52
     */
    public static byte[] decryptAsymmetric(String decryptStr, String privateKey) {
        RSA rsa = new RSA(privateKey, null);
        return rsa.decrypt(decryptStr, KeyType.PrivateKey);
    }


    /**
     * 对称
     * 解密
     * 1、AES
     *
     * @author lxp
     * @since 2023/4/23/023 17:52
     */
    public static String decryptSymmetryStr(String securityType, byte[] securityKey, byte[] securityOffSet, String encryptStr) {
        if ("AES".equals(securityType)) {
            AES aes = new AES(Mode.CBC, Padding.PKCS5Padding, securityKey, securityOffSet);
            return aes.decryptStr(encryptStr);
        } else {
            throw new BusinessException("未配置解密算法！");
        }
    }


    /**
     * 验签
     *
     * @author lxp
     * @since 2023/4/23/023 18:17
     */
    public static boolean verify(String securityType, String verifyStr, String signedStr, String verifyKey) {
        Console.log("verify. signText:{},sign:{},key:{}", verifyStr, signedStr, verifyKey);
        if (StrUtil.isBlank(signedStr)) {
            throw new BusinessException("签名为空！");
        }
        Sign sign = null;
        if ("RSA2".equals(securityType)) {
            sign = SecureUtil.sign(SignAlgorithm.SHA256withRSA, null, verifyKey);
        } else {
            throw new BusinessException("未配置验签算法！");
        }
        return sign.verify(verifyStr.getBytes(), Base64Decoder.decode(signedStr));
    }

}
